#pragma oncc

#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/time.h>
#include<sys/resource.h>
#include<signal.h>
#include"../comm/log.hpp"
#include"../comm/util.hpp"

namespace ns_runner
{
    using namespace ns_log;
    using namespace ns_util;
    class Runner
    {
        public:
        Runner()
        {}
        ~Runner()
        {}
        
        static void SetProcLimit(int _cpu,int _mem)
        {
            //设置CPU时长
           struct rlimit cpu_rlimit;
           cpu_rlimit.rlim_max=RLIM_INFINITY;
           cpu_rlimit.rlim_cur=_cpu;
           setrlimit(RLIMIT_CPU,&cpu_rlimit);
            
            //设置内存大小
           struct rlimit mem_rlimit;
           mem_rlimit.rlim_max=RLIM_INFINITY;
           mem_rlimit.rlim_cur=_mem*1024;  //以字节为单位
           setrlimit(RLIMIT_AS,&mem_rlimit);
        }

        //指明文件名即可，不需要带路径，因为形成的文件是在temp中，而在util.hpp
        //中，我们已经将./temp路径包含在其中。
        /******************************************
        *通过返回来判断，程序运行的异常
        *   返回值>0：程序异常了，退出的时候收到了信号，返回值就是信号的编号
        *   返回值==0：正常运行完毕的，结果都保存到了临时文件中
        *   返回值<0：内部错误，比如问问加你打开失败这一类
        *
        *cpu_limit:该程序使用的时候，可以使用最大的资源上线
        *mem_limit:该程序运行时，可以使用最大的内存大小
        *******************************************/

        static int Run(const std::string& file_name,int cpu_limit,int mem_limit)
        {
            //一个程序在启动的时候，会默认进行一下处理
            //标准输入：不处理
            //标准输出：程序运行完成，输出的结果
            //标准错误：运行时错误信息
            //只不过我们把这些信息通过三个临时文件来进行报错、传输。
            //所以这里我们还有把程序运行时候进行重定向，将信息保存到下面的文件

            std::string _execute=PathUtil::Exe(file_name);
            std::string _stdin=PathUtil::Stdin(file_name);
            std::string _stdout=PathUtil::Stdout(file_name);
            std::string _stderr=PathUtil::Stderr(file_name);
            //打开文件,文件描述会被子进程继承下去
            umask(0);
            int stdin_fd=open(_stdin.c_str(),O_CREAT | O_WRONLY,0644);
            int stdout_fd=open(_stdout.c_str(),O_CREAT | O_WRONLY,0644);
            int stderr_fd=open(_stderr.c_str(),O_CREAT | O_WRONLY,0644);
           
            if(stdin_fd<0||stdout_fd<0||stderr_fd<0)
            {
                LOG(ERROR)<<"运行时打开标准文件失败"<<std::endl;
                return -1;//代表文件打开失败
            }

            pid_t pid=fork();
            if(pid<0)
            {
                LOG(ERROR)<<"运行时创建子进程失败"<<std::endl;
               close(stdin_fd);
               close(stdout_fd);
               close(stderr_fd);
               return -2;//代表创建子进程失败
            }
            else if(pid==0)
            {
               //将此标准输出、输入、错误、重定向
               dup2(stdin_fd,0);
               dup2(stdout_fd,1);
               dup2(stderr_fd,2);
               SetProcLimit(cpu_limit,mem_limit);
               execl(_execute.c_str()/*谁要被执行*/,_execute.c_str()/*在命令行上该如何执行*/,nullptr);
               exit(1);
            }
            else
            {
               int status=0;
               close(stdin_fd);
               close(stdout_fd);
               close(stderr_fd);
                waitpid(pid,&status,0);
                std::cout<<status<<std::endl;
               //通过status来判断是否异常，有异常，会收到信号
               LOG(INFO)<<"运行完毕,info: "<<(status&0x7F)<<std::endl;
              
               return status & 0x7F; 
            }
        }
    };
}
